
//  Copyright (C) 2009-2011 Luca Piccioni
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Globalization;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace Derm
{
	/// <summary>
	/// Generic matrix.
	/// </summary>
	/// <remarks>
	/// <para>
	/// This class defines a generic matrix and the most common operations on it.
	/// </para>
	/// </remarks>
	public class Matrix : IEquatable<Matrix>, IXmlSerializable
	{
		#region Constructors

		/// <summary>
		/// Construct a matrix.
		/// </summary>
		/// <remarks>
		/// The matrix components are all zeroes.
		/// </remarks>
		/// <param name="c">
		/// A <see cref="System.UInt32"/> that specifies the matrix colum count.
		/// </param>
		/// <param name="r">
		/// A <see cref="System.UInt32"/> that specifies the matrix row count.
		/// </param>
		/// <exception cref="ArgumentException">
		/// Exception throw is <paramref name="c"/> or <paramref name="r"/> equals to 0.
		/// </exception>
		public Matrix(uint c, uint r)
		{
			if (c == 0)
				throw new ArgumentException("invalid", "c");
			if (r == 0)
				throw new ArgumentException("invalid", "r");

			// Store matrix extents
			mWidth = c; mHeight = r;
			// Allocate matrix
			MatrixBuffer = new float[mWidth * mHeight];
		}

		/// <summary>
		/// Construct a matrix that is a complement matrix of another matrix.
		/// </summary>
		/// <param name="m">
		/// A <see cref="Matrix"/>
		/// </param>
		/// <param name="c">
		/// A <see cref="System.UInt32"/> that specifies the matrix colum index to construct the complement matrix.
		/// </param>
		/// <param name="r">
		/// A <see cref="System.UInt32"/> that specifies the matrix row index to construct the complement matrix.
		/// </param>
		/// <exception cref="ArgumentNullException">
		/// Exception throw if <paramref name="m"/> is null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// Exception throw if <paramref name="c"/> is greater than <paramref name="m"/> column count, or if <paramref name="r"/>
		/// is greater than <paramref name="m"/> row count.
		/// </exception>
		public Matrix(Matrix m, uint c, uint r)
		{
			if (m == null)
				throw new ArgumentNullException("m");
			if ((m.mWidth <= 1) || (m.mHeight <= 1))
				throw new ArgumentException("too small", "m");
			if (c >= m.mWidth)
				throw new ArgumentException("out of bounds", "c");
			if (r >= m.mHeight)
				throw new ArgumentException("out of bounds", "r");

			// Store matrix extents
			mWidth = m.mWidth - 1; mHeight = m.mHeight - 1;
			// Allocate matrix
			MatrixBuffer = new float[mWidth * mHeight];
			// Copy complement matrix components (exclude colum c and row r)
			for (uint ic = 0; ic < mWidth; ic++)
				for (uint ir = 0; ir < mHeight; ir++)
					this[ic,ir] = m[(ic<c)?ic:ic+1,(ir<r)?ir:ir+1];
		}

		/// <summary>
		/// Construct a matrix which is a copy of another matrix.
		/// </summary>
		/// <param name="m">
		/// A <see cref="Matrix"/> to be copied.
		/// </param>
		/// <exception cref="ArgumentNullException">
		/// Exception throw if <paramref name="m"/> is null.
		/// </exception>
		public Matrix(Matrix m)
		{
			if (m == null)
				throw new ArgumentNullException("m");

			// Store matrix extents
			mWidth = m.mWidth; mHeight = m.mHeight;
			// Allocate matrix
			MatrixBuffer = new float[mWidth * mHeight];

			// Copy matrix components
			unsafe {
				fixed (float* matrix = MatrixBuffer)
				fixed (float* otherMatrix = m.MatrixBuffer)
				{
					uint length = mWidth * mHeight;

					for (int i = 0; i < length; i++)
						matrix[i] = otherMatrix[i];
				}
			}
		}

		/// <summary>
		/// Matrix components.
		/// </summary>
		protected internal float[] MatrixBuffer;

		#endregion

		#region Matrix Properties

		/// <summary>
		/// Matrix width (column count).
		/// </summary>
		public uint Width
		{
			get { return (mWidth); }
		}

		/// <summary>
		/// Matrix height (row count).
		/// </summary>
		public uint Height
		{
			get { return (mHeight); }
		}

		/// <summary>
		/// Determine whether this matrix is square.
		/// </summary>
		public bool IsSquare
		{
			get { return (Width == Height); }
		}

		/// <summary>
		/// Matrix width (column count).
		/// </summary>
		private readonly uint mWidth;

		/// <summary>
		/// Matrix height (row count).
		/// </summary>
		private readonly uint mHeight;

		#endregion

		#region Matrix Operators

		/// <summary>
		/// Matrix component indexer.
		/// </summary>
		/// <param name="c">
		/// A <see cref="System.Int32"/>
		/// </param>
		/// <param name="r">
		/// A <see cref="System.Int32"/>
		/// </param>
		/// <exception cref="ArgumentException">
		/// Exception throw if <paramref name="c"/> is greater than <see name="Matrix.Width"/> column count, or if <paramref name="r"/>
		/// is greater than <see name="Matrix.Height"/> row count.
		/// </exception>
		public float this[uint c, uint r]
		{
			get {
				if (c >= mWidth)
					throw new ArgumentException("colum index greater than column count", "c");
				if (r >= mHeight)
					throw new ArgumentException("row index greater than row count", "r");

				unsafe {
					fixed (float* matrix = MatrixBuffer) {
						return (matrix[c * mHeight + r]);
					}
				}
			}
			set {
				if (c >= mWidth)
					throw new ArgumentException("colum index greater than column count", "c");
				if (r >= mHeight)
					throw new ArgumentException("row index greater than row count", "r");
				unsafe {
					fixed (float* matrix = MatrixBuffer) {
						matrix[c * mHeight + r] = value;
					}
				}
			}
		}

		/// <summary>
		/// Sum two Matrix.
		/// </summary>
		/// <param name="m1">
		/// A <see cref="Matrix"/> that specifies the left Matrix.
		/// </param>
		/// <param name="m2">
		/// A <see cref="Matrix"/> that specifies the right Matrix.
		/// </param>
		/// <returns>
		/// A <see cref="Matrix"/> resulting from the sum of the two Matrix specified as parameters.
		/// </returns>
		/// <exception cref="ArgumentNullException">
		/// Exception throw if <paramref name="m1"/> or <paramref name="m2"/> is null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// Exception thrown if <paramref name="m1"/> width is different from <paramref name="m2"/> height.
		/// </exception>
		public static Matrix operator+(Matrix m1, Matrix m2)
		{
			if (m1 == null)
				throw new ArgumentNullException("m1");
			if (m2 == null)
				throw new ArgumentNullException("m2");
			if (m1.mWidth != m2.mHeight)
				throw new ArgumentException("matrices width/height mismatch");

			// Allocate product matrix
			Matrix sum = new Matrix(m1.mWidth, m2.mHeight);

			unsafe {
				fixed (float* matrix1 = m1.MatrixBuffer)
				fixed (float* matrix2 = m1.MatrixBuffer)
				fixed (float* sumMatrix = sum.MatrixBuffer)
				{
					uint length = m1.mWidth * m1.Height;

					for (uint i = 0; i < length; i++)
						sumMatrix[i] = matrix1[i] + matrix2[i];	
				}
				
			}

			return (sum);
		}

		/// <summary>
		/// Multiply a matrix with a scalar value.
		/// </summary>
		/// <param name="m">
		/// A <see cref="Matrix"/>
		/// </param>
		/// <param name="scalar">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <returns>
		/// A <see cref="Matrix"/>
		/// </returns>
		/// <exception cref="ArgumentNullException">
		/// Exception throw if <paramref name="m"/> is null.
		/// </exception>
		public static Matrix operator*(Matrix m, float scalar)
		{
			if (m == null)
				throw new ArgumentNullException("m");

			// Allocate product matrix
			Matrix prod = new Matrix(m.mWidth, m.mHeight);

			unsafe {
				fixed (float* matrix = m.MatrixBuffer)
				fixed (float* prodMatrix = prod.MatrixBuffer) {
					uint length = m.mWidth * m.Height;

					for (uint i = 0; i < length; i++)
						prodMatrix[i] = matrix[i] * scalar;	
				}
			}

			return (prod);
		}

		/// <summary>
		/// Divide a matrix with a scalar value.
		/// </summary>
		/// <param name="m">
		/// A <see cref="Matrix"/>
		/// </param>
		/// <param name="scalar">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <returns>
		/// A <see cref="Matrix"/>
		/// </returns>
		/// <exception cref="ArgumentNullException">
		/// Exception throw if <paramref name="m"/> is null.
		/// </exception>
		public static Matrix operator/(Matrix m, float scalar)
		{
			if (m == null)
				throw new ArgumentNullException("m");

			// Allocate product matrix
			Matrix div = new Matrix(m.mWidth, m.mHeight);

			unsafe {
				fixed (float* matrix = m.MatrixBuffer)
				fixed (float* divMatrix = div.MatrixBuffer)
				{
					uint length = m.mWidth * m.Height;

					for (uint i = 0; i < length; i++)
						divMatrix[i] = matrix[i] / scalar;
				}
			}

			return (div);
		}

		/// <summary>
		/// Multiply two Matrix.
		/// </summary>
		/// <param name="m">
		/// A <see cref="Matrix"/> that specifies the left multiplication operand.
		/// </param>
		/// <param name="n">
		/// A <see cref="Matrix"/> that specifies the right multiplication operand.
		/// </param>
		/// <returns>
		/// A <see cref="Matrix"/> resulting from the product of the matrix <paramref name="m"/> and the matrix <paramref name="n"/>.
		/// </returns>
		/// <exception cref="ArgumentNullException">
		/// Exception throw if <paramref name="m"/> or <paramref name="n"/> is null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// Exception thrown if <paramref name="m"/> width is different from <paramref name="n"/> height.
		/// </exception>
		public static Matrix operator*(Matrix m, Matrix n)
		{
			if (m == null)
				throw new ArgumentNullException("m");
			if (n == null)
				throw new ArgumentNullException("n");
			if (m.mWidth != n.mHeight)
				throw new ArgumentException("matrices width/height mismatch");

			// Allocate product matrix
			Matrix prod = new Matrix(m.mWidth, n.mHeight);

			unsafe {
				fixed (float* pm = m.MatrixBuffer)
				fixed (float* pn = n.MatrixBuffer)
				fixed (float* prodMatrix = prod.MatrixBuffer)
				{
					uint w = m.mWidth, h1 = m.Height, h2 = n.Height;

					// Compute matrix product
					for (uint r = 0; r < h1; r++) {
						for (uint c = 0; c < w; c++) {
							float s = 0.0f;

							for (uint i = 0; i < h1; i++)
								s += pm[i * h1 + r] * pn[c * h1 + i];

							prodMatrix[c * h1 + r] = s;
						}
					}
				}
			}

			return (prod);
		}

		/// <summary>
		/// Equality operator.
		/// </summary>
		/// <param name="m1"></param>
		/// <param name="m2"></param>
		/// <returns></returns>
		public static bool operator==(Matrix m1, Matrix m2)
		{
			return (Equals(m1, m2));
		}

		/// <summary>
		/// Inequality operator.
		/// </summary>
		/// <param name="m1"></param>
		/// <param name="m2"></param>
		/// <returns></returns>
		public static bool operator!=(Matrix m1, Matrix m2)
		{
			return (!Equals(m1, m2));
		}

		#endregion

		#region Matrix Functions

		/// <summary>
		/// Set to arbitrary matrix.
		/// </summary>
		/// <param name="m">
		/// A <see cref="Matrix"/>
		/// </param>
		/// <exception cref="ArgumentNullException">
		/// Exception thrown if <paramref name="m"/> is null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// Exception thrown if <paramref name="m"/> width and height are not equal to this Matrix width and height.
		/// </exception>
		public void Set(Matrix m)
		{
			if (m == null)
				throw new ArgumentNullException("m");
			if (m.mWidth != Width)
				throw new ArgumentException("matrix width mismatch");
			if (m.mHeight != Height)
				throw new ArgumentException("matrix height mismatch");

			unsafe {
				fixed (float* matrix = MatrixBuffer)
				fixed (float* otherMatrix = m.MatrixBuffer)
				{
					uint length = m.mWidth * m.Height;

					for (uint i = 0; i < length; i++)
						matrix[i] = otherMatrix[i];
				}
			}
		}

		/// <summary>
		/// Set to void matrix.
		/// </summary>
		public void SetVoid()
		{
			unsafe {
				fixed (float* matrix = MatrixBuffer)
				{
					uint length = mWidth * Height;

					for (uint i = 0; i < length; i++)
						matrix[i] = 0.0f;
				}
			}
		}

		/// <summary>
		/// Compute the transpose of this Matrix.
		/// </summary>
		/// <returns>
		/// A <see cref="Matrix"/> which hold the transpose of this Matrix.
		/// </returns>
		public Matrix Transpose()
		{
			Matrix t = Clone();

			// Transpose matrix
			for (uint c = 0; c < mWidth; c++)
				for (uint r = 0; r < mHeight; r++)
					t[r,c] = this[c,r];

			return (t);
		}

		/// <summary>
		/// Clone this Matrix.
		/// </summary>
		/// <returns>
		/// It returns a deep copy of this Matrix.
		/// </returns>
		public virtual Matrix Clone()
		{
			return (new Matrix(this));
		}

		#endregion

		#region Square Matrix Functions

		/// <summary>
		/// Set to identity matrix.
		/// </summary>
		/// <exception cref="InvalidOperationException">
		/// Exception thrown if this Matrix is not square.
		/// </exception>
		public void SetIdentity()
		{
			if (IsSquare == false)
				throw new InvalidOperationException("non-square");

			unsafe {
				fixed (float* matrix = MatrixBuffer)
				{
					for (uint c = 0; c < mWidth; c++)
						for (uint r = 0; r < mHeight; r++)
							if (c == r) matrix[c * mWidth + r] = 1.0f; else matrix[c * mWidth + r] = 0.0f;
				}
			}
		}

		/// <summary>
		/// Determine whether this matrix is an identity.
		/// </summary>
		/// <returns>
		/// It returns a boolean value indicating that this matrix is identity.
		/// </returns>
		public bool IsIdentity()
		{
			return (IsIdentity(1e-6f));
		}

		/// <summary>
		/// Determine whether this matrix is an identity.
		/// </summary>
		/// <param name="precision">
		/// A <see cref="System.Single"/> that specify the precision used for testing identity.
		/// </param>
		/// <returns>
		/// It returns a boolean value indicating that this matrix is identity.
		/// </returns>
		/// <remarks>
		/// <para>This method can be used for testing matrix identity with a specific range of approximation.</para>
		/// <para>For a reasonable precision for matrices having translations and rotations use <see cref="IsIdentity()"/>.</para>
		/// </remarks>
		public bool IsIdentity(float precision)
		{
			if (IsSquare == false)
				throw new InvalidOperationException("non-square");

			unsafe {
				fixed (float* matrix = MatrixBuffer)
				{
					for (uint c = 0; c < mWidth; c++) {
						for (uint r = 0; r < mHeight; r++) {
							float value = matrix[c * mWidth + r];

							if ((c != r) && (Math.Abs(value) > precision))
								return (false);
							if ((c == r) && (Math.Abs(1.0f - value) > Single.Epsilon))
								return (false);
						}
					}

					return (true);
				}
			}
		}

		/// <summary>
		/// Matrix determinant.
		/// </summary>
		/// <exception cref="InvalidOperationException">
		/// Exception thrown if this Matrix is not square.
		/// </exception>
		public virtual float GetDeterminant()
		{
			if (IsSquare == false)
				throw new InvalidOperationException("non-square");

			if (mWidth > 2) {
				float d = 0.0f;

				for (uint r = 0; r < Height; r++) {
					Matrix complement = new Matrix(this, 0, r);
					float sng = ((r % 2) == 0) ? +1.0f : -1.0f;

					d +=  sng * this[0, r] * complement.GetDeterminant();
				}

				return (d);
			} else {

				//     | a b |
				// m = | c d |
				//
				// det = ad - bc

				return (this[0, 0] * this[1, 1] - this[1, 0] * this[0, 1]);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="m"></param>
		/// <param name="c"></param>
		/// <param name="r"></param>
		/// <returns></returns>
		/// <exception cref="ArgumentNullException">
		/// Exception thrown if <paramref name="m"/> is null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// Exception throw if <paramref name="c"/> is greater than <paramref name="m"/> column count, or if <paramref name="r"/>
		/// is greater than <paramref name="m"/> row count.
		/// </exception>
		/// <exception cref="InvalidOperationException">
		/// Exception thrown if <paramref name="m"/> is not a square matrix.
		/// </exception>
		private static float GetComplement(Matrix m, uint c, uint r)
		{
			float sign;

			if (m == null)
				throw new ArgumentNullException("m");
			if (m.IsSquare == false)
				throw new InvalidOperationException("non-square");
			if (c >= m.mWidth)
				throw new ArgumentException("out of bounds", "c");
			if (r >= m.mHeight)
				throw new ArgumentException("out of bounds", "r");

			// Determine complement sign
			if (((c + r) % 2) == 0) sign = 1.0f; else sign = -1.0f;

			// Compute matrix component complement
			if (m.mWidth > 2) {
				Matrix complement = new Matrix(m, c, r);

				return (sign * complement.GetDeterminant());
			} else
				return (sign * m[(c==0)?1:(uint)0,(r==0)?1:(uint)0]);
		}

		/// <summary>
		/// Inverse Matrix of this Matrix.
		/// </summary>
		/// <returns>
		/// A <see cref="Matrix"/> representing the inverse matrix of this Matrix.
		/// </returns>
		/// <exception cref="InvalidOperationException">
		/// The exception is thrown if this Matrix is not square, or it's determinant is 0.0.
		/// </exception>
		public virtual Matrix GetInverseMatrix()
		{
			// Matrix shall be square
			if (IsSquare == false)
				throw new InvalidOperationException("non-square");

			// Matrix shall not be singular
			float d = GetDeterminant();
			if (Math.Abs(d) < Single.Epsilon)
				throw new InvalidOperationException("singular");

			// Compute inverse matrix
			Matrix iMatrix = Clone();

			for (uint c = 0; c < Width; c++)
				for (uint r = 0; r < Height; r++)
					iMatrix[r, c] = GetComplement(this, c, r) / d;

			return (iMatrix);
		}

		#endregion

		#region IEquatable<Matrix> Implementation

		/// <summary>
		/// Indicates whether the this Matrix is equal to another Matrix.
		/// </summary>
		/// <param name="other">
		/// A Matrix to compare with this object.
		/// </param>
		/// <returns>
		/// It returns true if the this Matrix is equal to <paramref name="other"/>; otherwise, false.
		/// </returns>
		public bool Equals(Matrix other)
		{
			if (ReferenceEquals(null, other))
				return (false);
			if (ReferenceEquals(this, other))
				return (true);
			if ((mWidth != other.Width) || (mHeight != other.mHeight))
				return (false);

			unsafe {
				fixed (float* m1Fix = MatrixBuffer)
				fixed (float* m2Fix = other.MatrixBuffer)
				{
					uint w = mWidth, h = Height;

					// Compute matrix product
					for (uint c = 0; c < w * h; c++) {
						if (Math.Abs(m1Fix[c] - m2Fix[c]) > 1e-6f) 
							return (false);
					}
				}
			}

			return (true);
		}

		/// <summary>
		/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
		/// </summary>
		/// <param name="obj">
		/// The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.
		/// </param>
		/// <returns>
		/// It returns true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
		/// </returns>
		public override bool Equals(object obj)
		{
			if (ReferenceEquals(null, obj))
				return (false);
			if (ReferenceEquals(this, obj))
				return (true);
			if ((obj.GetType() != typeof(Matrix)) && (obj.GetType().IsSubclassOf(typeof(Matrix)) == false))
				return (false);

			return (Equals((Matrix)obj));
		}

		/// <summary>
		/// Serves as a hash function for a particular type. <see cref="M:System.Object.GetHashCode"/> is suitable for
		/// use in hashing algorithms and data structures like a hash table.
		/// </summary>
		/// <returns>
		/// A hash code for the current <see cref="T:System.Object"/>.
		/// </returns>
		public override int GetHashCode()
		{
			unchecked {
				int result = MatrixBuffer.GetHashCode();

				result = (result * 397) ^ mWidth.GetHashCode();

				result = (result * 397) ^ mHeight.GetHashCode();

				return result;
			}
		}

		#endregion

		#region IXmlSerializable Implementation

		/// <summary>
		/// This property is reserved, apply the System.Xml.Serialization.XmlSchemaProviderAttribute to the class instead.
		/// </summary>
		/// <returns>
		/// An System.Xml.Schema.XmlSchema that describes the XML representation of the object that is produced by the
		/// System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter) method and consumed by the
		/// System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader) method.
		/// </returns>
		public XmlSchema GetSchema() { return (null); }

		/// <summary>
		/// Generates an object from its XML representation.
		/// </summary>
		/// <param name="reader">
		/// The System.Xml.XmlReader stream from which the object is deserialized.
		/// </param>
		public virtual void ReadXml(XmlReader reader)
		{

		}

		/// <summary>
		/// Converts an object into its XML representation.
		/// </summary>
		/// <param name="writer">
		/// The System.Xml.XmlWriter stream to which the object is serialized.
		/// </param>
		public virtual void WriteXml(XmlWriter writer)
		{
			// Attribute: columns count
			writer.WriteAttributeString(sXmlColumnsAttribute, Width.ToString(NumberFormatInfo.InvariantInfo));
			// Attribute: rows count
			writer.WriteAttributeString(sXmlColumnsAttribute, Height.ToString(NumberFormatInfo.InvariantInfo));

			// Write matrix components
			for (uint c = 0; c < Width; c++) {
				writer.WriteStartElement(sXmlColumnElement);
				for (uint r = 0; r < Height; r++)
					writer.WriteElementString(sXmlColumnValueElement, this[c, r].ToString(NumberFormatInfo.InvariantInfo));
				writer.WriteEndElement();
			}

			// Example of output:
			//
			// <Matrix Columns="2" Rows="4">
			//   <Column>
			//     <Value>1.0</Value>
			//     <Value>0.0</Value>
			//     <Value>0.0</Value>
			//     <Value>0.0</Value>
			//   </Column>
			//   <Column>
			//     <Value>0.0</Value>
			//     <Value>1.0</Value>
			//     <Value>0.0</Value>
			//     <Value>0.0</Value>
			//   </Column>
			// </Matrix>
		}

		/// <summary>
		/// Name of the attribute that specify the matrix columns count.
		/// </summary>
		private static readonly string sXmlColumnsAttribute = "Columns";

		/// <summary>
		/// Name of the attribute that specify the matrix rows count.
		/// </summary>
		private static readonly string sXmlRowsAttribute = "Rows";

		/// <summary>
		/// Name of the element that specify the matrix column.
		/// </summary>
		private static readonly string sXmlColumnElement = "Column";

		/// <summary>
		/// Name of the element that specify the matrix column values.
		/// </summary>
		private static readonly string sXmlColumnValueElement = "Value";

		#endregion

		#region Object Overrides

		/// <summary>
		/// Stringify this Vertex3f.
		/// </summary>
		/// <returns>
		/// Returns a <see cref="System.String"/> that represents this Vertex3f.
		/// </returns>
		public override string ToString()
		{
			StringBuilder sb = new StringBuilder();

			sb.AppendFormat("Matrix{0}x{1}:", Width, Height);
			for (uint c = 0; c < Width; c++) {
				sb.Append(" [");
				for (uint r = 0; r < Height; r++)
					sb.AppendFormat("{0}{1}", this[c, r], (r < Height - 1) ? ", " : String.Empty);
				sb.Append("]");
			}

			return (sb.ToString());
		}

		#endregion
	}
}
